using System;
using System.Globalization;
using System.IO;

namespace Atomic.Vasp.Options
{
	/// <summary>
	/// Base class for ionic relaxation options (IBRION=2 and ISIF).
	/// http://cms.mpi.univie.ac.at/vasp/guide/node110.html
	/// http://cms.mpi.univie.ac.at/vasp/guide/node112.html
	/// </summary>
	[Serializable]
	public class IonicRelaxation : VaspOption
	{
		public IonicRelaxation(bool ions, bool cellShape, bool cellVolume, int steps)
			: this(ions, cellShape, cellVolume, steps, IonicRelaxationAlgorithm.ConjugateGradient)
		{
		}

		public IonicRelaxation(bool ions, bool cellShape, bool cellVolume, int steps, IonicRelaxationAlgorithm algorithm)
			: this(ions, cellShape, cellVolume, steps, IonicRelaxationAlgorithm.ConjugateGradient, null)
		{
		}

		public IonicRelaxation(bool ions, bool cellShape, bool cellVolume, int steps, IonicRelaxationAlgorithm algorithm, double? scalingConstant)
		{
			Ions = ions;
			CellShape = cellShape;
			CellVolume = cellVolume;
			Steps = steps;
			Algorithm = algorithm;
			ScalingConstant = scalingConstant;
		}

		public override void Write(VaspIncar incar, DirectoryInfo directory)
		{
			if (!Ions && !CellShape && !CellVolume)
			{
				// Nothing done. Ignore.
				return;
			}

			switch (Algorithm)
			{
				case IonicRelaxationAlgorithm.QuasiNewton:
					incar.Add("IBRION", "1", "Quasi-Newton algorithm to relax ions");
					break;

				case IonicRelaxationAlgorithm.ConjugateGradient:
					incar.Add("IBRION", "2", "Conjugate-gradient algorithm to relax ions");
					break;

				default:
					throw new NotSupportedException();
			}

			if (Ions)
			{
				if (!CellShape && !CellVolume)
				{
					incar.Add("ISIF", "2", "Relax ion positions only");
				}
				else if (CellShape && CellVolume)
				{
					incar.Add("ISIF", "3", "Relax ion positions, cell shape, and cell volume");
				}
				else if (CellShape && !CellVolume)
				{
					incar.Add("ISIF", "4", "Relax ion positions and cell shape");
				}
				else
				{
					throw new NotSupportedException();
				}
			}
			else
			{
				if (CellShape && !CellVolume)
				{
					incar.Add("ISIF", "5", "Relax cell shape");
				}
				else if (CellShape && CellVolume)
				{
					incar.Add("ISIF", "6", "Relax cell shape and cell volume");
				}
				else if (!CellShape && CellVolume)
				{
					incar.Add("ISIF", "7", "Relax cell volume");
				}
				else
				{
					throw new NotSupportedException();
				}
			}

			incar.Add("NSW", Steps.ToString(), "Maximum number of ionic steps");

			if (ScalingConstant.HasValue)
			{
				// POTIM serves as a scaling constant in all minimization algorithms (quasi-Newton, conjugate gradient, and damped molecular dynamics).
				// http://cms.mpi.univie.ac.at/vasp/vasp/POTIM_tag.html
				// http://cms.mpi.univie.ac.at/vasp-forum/forum_viewtopic.php?4.96
				incar.Add("POTIM", ScalingConstant.Value.ToString(CultureInfo.InvariantCulture));
			}
		}

		public bool Ions
		{
			get;
			private set;
		}

		public bool CellShape
		{
			get;
			private set;
		}

		public bool CellVolume
		{
			get;
			private set;
		}

		public IonicRelaxationAlgorithm Algorithm
		{
			get;
			private set;
		}

		/// <summary>
		/// Maximum number of ionic steps (NSW). At each step at most NELM electronic-SC loops are performed unless
		/// the EDIFF convergence criterium is matched before.
		/// http://cms.mpi.univie.ac.at/vasp/vasp/NSW_tag.html
		/// </summary>
		public int Steps
		{
			get;
			private set;
		}

		public double? ScalingConstant
		{
			get;
			private set;
		}
	}
}
